from odoo import api, fields, models, _
import io
import base64
import unicodedata
from odoo.exceptions import UserError
try:
    import xlsxwriter
except Exception:  # pragma: no cover
    xlsxwriter = None


class DocumentsProformaQuotationWizard(models.TransientModel):
    _name = "documents.proforma_quotation.wizard"
    _description = "报价单向导"

    language_mode = fields.Selection(
        selection=[("zh", "中文"), ("en", "英文"), ("it", "意大利文")],
        string="语言",
        default="zh",
        required=True,
    )
    export_format = fields.Selection(
        selection=[("xlsx", "XLSX")],
        string="导出格式",
        default="xlsx",
        required=True,
    )
    date = fields.Date(string="日期", default=fields.Date.context_today, required=True)
    company_id = fields.Many2one(
        comodel_name="res.company",
        string="公司",
        default=lambda self: self.env.company,
        required=True,
    )
    partner_id = fields.Many2one("res.partner", string="客户")
    sale_id = fields.Many2one("sale.order", string="报价单", domain=[("state", "in", ["draft", "sent"])], required=True)
    # 币种与汇率
    currency_id = fields.Many2one(
        comodel_name="res.currency",
        string="币种",
        default=lambda self: self.env.company.currency_id,
        required=True,
    )
    manual_rate = fields.Float(string="汇率", help="例如：1 EUR = 8.33 CNY，则填 8.33")
    show_manual_rate = fields.Boolean(string="显示汇率", compute="_compute_show_manual_rate")
    note = fields.Text(string="备注")

    # Generated file for download (align with packing_list)
    file_name = fields.Char(string="文件名", readonly=True)
    file_data = fields.Binary(string="文件", readonly=True)

    @api.onchange("sale_id")
    def _onchange_sale_id(self):
        if self.sale_id:
            self.partner_id = self.sale_id.partner_id.id
            # Prefill date from the quotation's date_order (user timezone)
            if self.sale_id.date_order:
                try:
                    export_date = fields.Datetime.context_timestamp(self, self.sale_id.date_order).date()
                except Exception:
                    export_date = fields.Date.to_date(self.sale_id.date_order)
                self.date = export_date
            # 币种取报价单币种，若无则公司币种
            self.currency_id = self.sale_id.currency_id or self.env.company.currency_id
        else:
            self.partner_id = False

    @api.onchange("currency_id", "date", "company_id")
    def _onchange_currency_or_date(self):
        if self.currency_id and self.company_id:
            company_currency = self.company_id.currency_id
            if self.currency_id != company_currency:
                rate = self.env['res.currency.rate'].search([
                    ('currency_id', '=', self.currency_id.id),
                    ('name', '<=', self.date)
                ], order='name desc', limit=1)
                if rate:
                    self.manual_rate = rate.rate
                else:
                    self.manual_rate = self.currency_id.rate
            else:
                self.manual_rate = 1.0

    @api.depends("currency_id", "company_id")
    def _compute_show_manual_rate(self):
        for wizard in self:
            wizard.show_manual_rate = bool(
                wizard.currency_id
                and wizard.company_id
                and wizard.currency_id != wizard.company_id.currency_id
            )

    # ------------------------- Helpers -------------------------
    def _i18n_labels(self, lang):
        labels = {
            "zh": {
                "title": "报价单",
                "date": "报价单日期",
                "number": "编号",
                "seq": "序号",
                "product": "产品",
                "desc": "描述",
                "qty": "数量",
                "unit_price": "单价",
                "amount": "金额",
                "tax": "税项",
                "total_qty": "总数量",
                "subtotal": "小计",
                "tax_total": "税项",
                "grand_total": "含税总额",
                "company": "公司",
                "customer": "客户",
                "salesperson": "销售员",
                "currency": "币种",
                "exchange_rate": "汇率",
            },
            "en": {
                "title": "Quotation",
                "date": "Quotation Date",
                "number": "Number",
                "seq": "Seq",
                "product": "Product",
                "desc": "Description",
                "qty": "Quantity",
                "unit_price": "Unit Price",
                "amount": "Amount",
                "tax": "VAT Rate",
                "total_qty": "Total Quantity",
                "subtotal": "Subtotal",
                "tax_total": "Tax",
                "grand_total": "Total",
                "company": "Company",
                "customer": "Customer",
                "salesperson": "Salesperson",
                "currency": "Currency",
                "exchange_rate": "Exchange Rate",
            },
            "it": {
                "title": "Preventivo",
                "date": "Data",
                "number": "Numero",
                "seq": "N°",
                "product": "Prodotto",
                "desc": "Descrizione",
                "qty": "Quantità",
                "unit_price": "Prezzo unitario",
                "amount": "Importo",
                "tax": "IVA",
                "total_qty": "Quantità totale",
                "subtotal": "Imponibile",
                "tax_total": "IVA",
                "grand_total": "Totale",
                "company": "Azienda",
                "customer": "Cliente",
                "salesperson": "Venditore",
                "currency": "Valuta",
                "exchange_rate": "Cambio",
            },
        }
        return labels.get(lang or "zh")

    def _display_width(self, text):
        s = str(text or "")
        width = 0
        for ch in s:
            eaw = unicodedata.east_asian_width(ch)
            width += 2 if eaw in ("F", "W") else 1
        return width

    def _map_to_odoo_lang(self, lang_code):
        mapping = {
            "zh": "zh_CN",
            "en": "en_US",
            "it": "it_IT",
        }
        return mapping.get(lang_code, "zh_CN")

    def _get_valid_odoo_lang(self, lang_code):
        """Return lang_code only if installed in this DB; else return None."""
        if not lang_code:
            return None
        Lang = self.env["res.lang"]
        rec = Lang.search([("code", "=", lang_code)], limit=1)
        return rec.code if rec else None

    def _get_tax_rate_percent(self, line):
        percent = 0.0
        for tax in line.tax_id:
            if getattr(tax, "amount_type", "percent") == "percent":
                percent += tax.amount
        return percent

    # ------------------------- Export -------------------------
    def action_export(self):
        self.ensure_one()
        if self.export_format != "xlsx":
            return {"type": "ir.actions.act_window_close"}
        if not xlsxwriter:
            raise UserError(_("The Python package 'xlsxwriter' is required to export XLSX files."))
        order = self.sale_id
        if not order:
            raise UserError(_("Please select a quotation."))

        lang = self.language_mode or "zh"
        L = self._i18n_labels(lang)
        odoo_lang = self._get_valid_odoo_lang(self._map_to_odoo_lang(lang))

        # Lines (skip sections/notes)
        order_lines = order.order_line.filtered(lambda l: not l.display_type)

        output = io.BytesIO()
        workbook = xlsxwriter.Workbook(output, {"in_memory": True})
        sheet = workbook.add_worksheet(L["title"])  # localized sheet name
        # 打印设置：A4 纵向，按页宽缩放，水平居中，边距
        try:
            sheet.set_portrait()
            sheet.set_paper(9)  # A4
            sheet.fit_to_pages(1, 0)
            sheet.center_horizontally()
            sheet.set_margins(left=0.3, right=0.3, top=0.6, bottom=0.8)
        except Exception:
            pass

        # Styles
        fmt_title = workbook.add_format({"bold": True, "font_size": 20, "align": "center", "valign": "vcenter"})
        fmt_subtitle = workbook.add_format({"bold": True, "font_size": 12})
        fmt_header = workbook.add_format({"bold": True, "align": "center", "valign": "vcenter", "border": 1})
        fmt_cell = workbook.add_format({"align": "left", "valign": "vcenter", "border": 1})
        fmt_cell_center = workbook.add_format({"align": "center", "valign": "vcenter", "border": 1})
        fmt_cell_left = workbook.add_format({"align": "left", "valign": "vcenter"})
        # Currency-aware formats
        cur = self.currency_id or order.currency_id or self.company_id.currency_id
        symbol = (cur and cur.symbol) or ""
        position = (cur and getattr(cur, "position", "before")) or "before"
        symbol_escaped = (symbol or "").replace('"', '""')
        if position == "after":
            num_format_curr = f"#,##0.00\"{symbol_escaped}\""
        else:
            num_format_curr = f"\"{symbol_escaped}\"#,##0.00"
        fmt_num = workbook.add_format({"num_format": num_format_curr, "align": "right", "valign": "vcenter", "border": 1})
        fmt_tot_label = workbook.add_format({"align": "right", "bold": True})
        fmt_tot_num = workbook.add_format({"num_format": num_format_curr, "align": "right", "bold": True})
        fmt_note = workbook.add_format({"italic": True})

        # Exchange rate for display calculation
        display_rate = self.manual_rate or 1.0

        # Column widths (dynamic based on headers and data) — add Currency after Qty
        headers_w = [L["seq"], L["number"], L["desc"], L["qty"], L["currency"], L["unit_price"], L["tax"], L["amount"]]
        base_widths = [6, 18, 36, 12, 10, 14, 10, 16]
        caps = [24, 28, 60, 14, 14, 20, 12, 24]
        header_widths = [max(base_widths[i], self._display_width(h) + 2) for i, h in enumerate(headers_w)]

        # Pre-scan data to estimate widths
        max_idx_w = self._display_width(str(max(1, len(order_lines)))) + 2
        max_code_w = 0
        max_desc_w = 0
        max_qty_w = 0
        max_tax_w = self._display_width("100%") + 2
        max_cur_w = self._display_width((cur and cur.name) or "") + 2
        max_unit_w = 0
        max_amt_w = 0
        for line in order_lines:
            # Product code mapping: prefer sale.order.line.new_serial_number
            line_serial = getattr(line, "new_serial_number", None)
            # 修改产品编码获取逻辑：优先条形码，然后line_serial，然后default_code
            product_code = line.product_id.product_tmpl_id.barcode or line_serial or (line.product_id.default_code or "")
            if odoo_lang:
                desc = line.product_id.with_context(lang=odoo_lang).name or (line.name or "")
            else:
                desc = line.name or (line.product_id.name or "")
            qty = line.product_uom_qty
            unit_price = line.price_unit
            tax_rate = self._get_tax_rate_percent(line)
            amount = line.price_total

            max_code_w = max(max_code_w, self._display_width(product_code) + 2)
            max_desc_w = max(max_desc_w, self._display_width(desc) + 2)
            max_qty_w = max(max_qty_w, self._display_width(f"{qty:,.2f}") + 2)
            unit_str = f"{abs(unit_price * display_rate):,.2f}"
            amt_str = f"{abs(amount * display_rate):,.2f}"
            if position == "after":
                unit_disp = f"{unit_str}{symbol}"
                amt_disp = f"{amt_str}{symbol}"
            else:
                unit_disp = f"{symbol}{unit_str}"
                amt_disp = f"{symbol}{amt_str}"
            max_unit_w = max(max_unit_w, self._display_width(unit_disp) + 2)
            max_amt_w = max(max_amt_w, self._display_width(amt_disp) + 2)
            max_tax_w = max(max_tax_w, self._display_width(f"{tax_rate:.0f}%") + 2)

        data_widths = [max_idx_w, max_code_w, max_desc_w, max_qty_w, max_cur_w, max_unit_w, max_tax_w, max_amt_w]
        # Ensure column A can fit the "Total Quantity" label text
        data_widths[0] = max(data_widths[0], self._display_width(f"{L['total_qty']}:") + 2)
        final_widths = [min(max(header_widths[i], data_widths[i], base_widths[i]), caps[i]) for i in range(8)]
        for col_idx, width in enumerate(final_widths):
            col_letter = chr(ord("A") + col_idx)
            sheet.set_column(f"{col_letter}:{col_letter}", width)

        # Header / Title
        # Title on the very top centered across columns
        sheet.merge_range(0, 0, 0, 7, L["title"], fmt_title)
        row = 1
        # Company and Customer
        company_name = (self.company_id.display_name or self.company_id.name or "")
        customer_name = (self.partner_id.display_name or self.partner_id.name or "")
        sheet.write(row, 0, f"{L['company']}: {company_name}")
        sheet.write(row, 3, f"{L['customer']}: {customer_name}")
        row += 1
        # Date and Salesperson
        salesperson = order.user_id and order.user_id.name or ""
        if order.date_order:
            try:
                export_date = fields.Datetime.context_timestamp(self, order.date_order).date()
            except Exception:
                export_date = fields.Date.to_date(order.date_order)
        else:
            export_date = self.date
        sheet.write(row, 0, f"{L['date']}: {fields.Date.to_string(export_date)}")
        sheet.write(row, 3, f"{L['salesperson']}: {salesperson}")
        row += 2

        # Before headers: print currency only (no exchange rate)
        # try:
        #     row -= 1
        #     currency_code = (cur and cur.name) or ""
        #     sheet.write(row, 3, f"{L['currency']}: {currency_code}")
        # except Exception:
        #     pass

        # Table headers (use 'number' for product code column per request)
        start_table_row = row + 1
        headers = [L["seq"], L["number"], L["desc"], L["qty"], L["currency"], L["unit_price"], L["tax"], L["amount"]]
        for col, head in enumerate(headers):
            sheet.write(start_table_row, col, head, fmt_header)
        row = start_table_row + 1

        # Body
        total_qty = 0.0
        for idx, line in enumerate(order_lines, start=1):
            # Product code mapping: prefer sale.order.line.new_serial_number
            line_serial = getattr(line, "new_serial_number", None)
            # 修改产品编码获取逻辑：优先条形码，然后line_serial，然后default_code
            product_code = line.product_id.product_tmpl_id.barcode or line_serial or (line.product_id.default_code or "")
            if odoo_lang:
                description = line.product_id.with_context(lang=odoo_lang).name or (line.name or "")
            else:
                description = line.name or (line.product_id.name or "")
            qty = line.product_uom_qty
            unit_price = line.price_unit
            line_tax_rate = self._get_tax_rate_percent(line)
            # Use currency-aware numbers
            amount = line.price_total

            total_qty += qty

            sheet.write(row, 0, idx, fmt_cell_center)
            sheet.write(row, 1, product_code, fmt_cell)
            sheet.write(row, 2, description, fmt_cell)
            sheet.write_number(row, 3, qty, fmt_cell_center)
            sheet.write(row, 4, (cur and cur.name) or '', fmt_cell_center)
            sheet.write_number(row, 5, unit_price * display_rate, fmt_num)
            sheet.write(row, 6, f"{line_tax_rate:.0f}%", fmt_cell_center)
            sheet.write_number(row, 7, amount * display_rate, fmt_num)
            row += 1

        # Total quantity
        row += 1
        sheet.merge_range(row, 0, row, 1, f"{L['total_qty']}:")
        sheet.write_number(row, 2, total_qty, fmt_cell_left)

        # Totals box
        subtotal = sum(order_lines.mapped("price_subtotal")) * display_rate
        tax_amount = sum(order_lines.mapped("price_tax")) * display_rate
        grand_total = sum(order_lines.mapped("price_total")) * display_rate

        totals_row = start_table_row + max(1, len(order_lines)) + 2
        label_col = 6
        value_col = 7
        sheet.write(totals_row, label_col, L["subtotal"], fmt_tot_label)
        sheet.write_number(totals_row, value_col, subtotal, fmt_tot_num)
        sheet.write(totals_row + 1, label_col, L["tax_total"], fmt_tot_label)
        sheet.write_number(totals_row + 1, value_col, tax_amount, fmt_tot_num)
        sheet.write(totals_row + 2, label_col, L["grand_total"], fmt_tot_label)
        sheet.write_number(totals_row + 2, value_col, grand_total, fmt_tot_num)

        if self.note:
            sheet.write(totals_row + 4, 0, self.note, fmt_note)

        workbook.close()
        output.seek(0)
        data = output.read()

        filename = f"{L['title']}_{order.name}.xlsx"
        self.write({
            "file_name": filename,
            "file_data": base64.b64encode(data),
        })

        return {
            "type": "ir.actions.act_url",
            "url": (
                f"/web/content/?model={self._name}&id={self.id}"
                f"&field=file_data&filename_field=file_name&download=true"
            ),
            "target": "self",
        } 